package org.example.service.impl;

import dev.ai4j.openai4j.Json;
import dev.langchain4j.data.embedding.Embedding;
import dev.langchain4j.data.segment.TextSegment;
import dev.langchain4j.model.Tokenizer;
import dev.langchain4j.model.openai.OpenAiTokenizer;
import dev.langchain4j.service.AiServices;
import dev.langchain4j.store.embedding.redis.RedisEmbeddingStore;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.interactive.documentnavigation.destination.PDPageDestination;
import org.apache.pdfbox.pdmodel.interactive.documentnavigation.outline.PDOutlineItem;
import org.apache.pdfbox.pdmodel.interactive.documentnavigation.outline.PDOutlineNode;
import org.apache.pdfbox.text.PDFTextStripper;
import org.example.config.RedisConfig;
import org.example.domain.vo.SectionInfo;
import org.example.result.Result;
import org.example.service.Interviewer;
import org.example.service.PdfLoaderService;
import org.example.util.CreateModel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import redis.clients.jedis.Jedis;
import dev.langchain4j.rag.content.Content;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Service
public class PdfLoaderServiceImpl implements PdfLoaderService {
    private static final int CHAT_MODEL_MAX_TOKENS = 8192;
    private static final int SAFETY_TOKEN_MARGIN = 300;
    private static final int MAX_THREADS = 4;
    
    // 用于检测目录层次的模式
    private static final Pattern SECTION_PATTERN = Pattern.compile("(\\d+(\\.\\d+)*)");

    @Autowired
    public RedisConfig redisConfig;
    @Autowired
    public CreateModel createModel;

    /**
     * 遍历PDF目录结构，收集所有章节信息
     */
    private void collectAllSections(PDOutlineNode node, Map<String, SectionInfo> sections,
                                 PDDocument document, String parentPath) throws IOException {
        PDOutlineItem current = node.getFirstChild();
        while (current != null) {
            String title = current.getTitle();
            String currentPath = parentPath.isEmpty() ? title : parentPath + "/" + title;
            
            // 创建当前节点的SectionInfo
            SectionInfo section = createSectionInfo(current, document, currentPath);
            if (section != null) {
                // 获取节点级别 (1.1, 1.1.1 等)
                int level = countDotsInTitle(title) + 1;
                section.setLevel(level);
                sections.put(currentPath, section);
            }
            
            // 继续处理子节点
            if (current.hasChildren()) {
                collectAllSections(current, sections, document, currentPath);
            }
            
            current = current.getNextSibling();
        }
    }
    
    /**
     * 计算标题中的点数来确定层级
     */
    private int countDotsInTitle(String title) {
        Matcher matcher = SECTION_PATTERN.matcher(title);
        if (matcher.find()) {
            String sectionNumber = matcher.group(1);
            return (int) sectionNumber.chars().filter(ch -> ch == '.').count();
        }
        return 0;
    }

    private SectionInfo createSectionInfo(PDOutlineItem item, PDDocument document, String fullPath)
            throws IOException {
        if (item.getDestination() instanceof PDPageDestination) {
            PDPageDestination dest = (PDPageDestination) item.getDestination();
            PDPage page = dest.getPage();
            int pageIndex = document.getPages().indexOf(page);

            SectionInfo section = new SectionInfo();
            section.setTitle(item.getTitle());
            section.setFullPath(fullPath);
            section.setStartPage(pageIndex);

            // 计算结束页 - 下一个同级节点的开始页面减1或文档结束
            int endPage = document.getNumberOfPages() - 1;
            PDOutlineItem nextSibling = item.getNextSibling();
            if (nextSibling != null && nextSibling.getDestination() instanceof PDPageDestination) {
                PDPageDestination nextDest = (PDPageDestination) nextSibling.getDestination();
                endPage = document.getPages().indexOf(nextDest.getPage()) - 1;
            }
            
            // 如果有子节点，结束页可能更早
            if (item.hasChildren()) {
                PDOutlineItem lastChild = findLastChild(item);
                if (lastChild != null && lastChild.getNextSibling() != null &&
                    lastChild.getNextSibling().getDestination() instanceof PDPageDestination) {
                    PDPageDestination childNextDest = (PDPageDestination) lastChild.getNextSibling().getDestination();
                    int childEndPage = document.getPages().indexOf(childNextDest.getPage()) - 1;
                    if (childEndPage < endPage) {
                        endPage = childEndPage;
                    }
                }
            }
            
            section.setEndPage(endPage);

            // 验证页码有效性
            if (section.getStartPage() > section.getEndPage()) {
                System.err.println("无效的页面范围: " + fullPath + " (" + 
                                  section.getStartPage() + "-" + section.getEndPage() + ")");
                return null;
            }

            return section;
        }
        return null;
    }
    
    /**
     * 找到节点的最后一个子节点
     */
    private PDOutlineItem findLastChild(PDOutlineItem parent) {
        if (!parent.hasChildren()) {
            return null;
        }
        
        PDOutlineItem child = parent.getFirstChild();
        PDOutlineItem lastChild = null;
        
        while (child != null) {
            lastChild = child;
            child = child.getNextSibling();
        }
        
        return lastChild;
    }

    private String extractSectionContent(PDDocument document, SectionInfo section)
            throws IOException {
        PDFTextStripper stripper = new PDFTextStripper();
        stripper.setStartPage(section.getStartPage() + 1); // PDFBox页码从1开始
        stripper.setEndPage(section.getEndPage() + 1);
        return stripper.getText(document).trim();
    }

    private void storeInRedis(Jedis jedis, String key, String content) {
        try {
            jedis.set(key, content);
            System.out.println("成功存储: " + key);
        } catch (Exception e) {
            System.err.println("Redis存储失败: " + e.getMessage());
        }
    }

    @Override
    public Result<String> exportPdfSectionsToRedis() {
        long startTime = System.currentTimeMillis();
        System.out.println("开始处理PDF...");
        
        Interviewer interviewer = AiServices.create(Interviewer.class, createModel.createChatLanguageModel());
        RedisEmbeddingStore redisEmbeddingStore = RedisEmbeddingStore.builder()
                .host("127.0.0.1")
                .port(6379)
                .dimension(1536)
                .build();

        try (PDDocument document = PDDocument.load(new File("E:\\offer-1.pdf"));
             Jedis jedis = new Jedis(redisConfig.getHost(), redisConfig.getPort())) {

            PDOutlineNode outline = document.getDocumentCatalog().getDocumentOutline();
            if (outline == null) {
                throw new IOException("PDF文档缺少目录结构");
            }

            // 收集所有章节信息
            Map<String, SectionInfo> allSections = new HashMap<>();
            collectAllSections(outline, allSections, document, "");
            System.out.println("找到 " + allSections.size() + " 个章节");
            
            // 根据章节级别过滤和处理
            List<SectionInfo> sectionsToProcess = new ArrayList<>();
            Map<String, List<String>> parentToChildrenMap = new HashMap<>();
            
            // 1. 将章节按父子关系组织
            for (SectionInfo section : allSections.values()) {
                String fullPath = section.getFullPath();
                int lastSlashIndex = fullPath.lastIndexOf('/');
                
                if (lastSlashIndex != -1) {
                    String parentPath = fullPath.substring(0, lastSlashIndex);
                    parentToChildrenMap.computeIfAbsent(parentPath, k -> new ArrayList<>())
                                       .add(fullPath);
                }
            }
            
            // 2. 选择要处理的章节 - 根据目录层级选择
            for (SectionInfo section : allSections.values()) {
                String path = section.getFullPath();
                
                // 获取子节点
                List<String> children = parentToChildrenMap.getOrDefault(path, new ArrayList<>());
                
                // 如果是叶子节点或子节点是深一层级的目录，则处理
                boolean shouldProcess = children.isEmpty(); // 叶子节点
                
                if (!shouldProcess && !children.isEmpty()) {
                    // 检查子节点级别
                    boolean allChildrenDeeper = true;
                    for (String childPath : children) {
                        SectionInfo childSection = allSections.get(childPath);
                        if (childSection != null && 
                            (childSection.getLevel() <= section.getLevel() + 1)) {
                            allChildrenDeeper = false;
                            break;
                        }
                    }
                    shouldProcess = allChildrenDeeper;
                }
                
                if (shouldProcess) {
                    sectionsToProcess.add(section);
                }
            }
            
            System.out.println("将处理 " + sectionsToProcess.size() + " 个章节");
            
            // 创建线程池用于并行处理
            ExecutorService executor = Executors.newFixedThreadPool(MAX_THREADS);
            List<CompletableFuture<Void>> futures = new ArrayList<>();
            
            AtomicInteger processedSections = new AtomicInteger(0);
            
            // 并行处理每个章节
            for (SectionInfo section : sectionsToProcess) {
                CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
                    try {
                        // 提取整个章节内容
                        String content = extractSectionContent(document, section);
                        if (content.isEmpty()) {
                            System.out.println("章节内容为空: " + section.getFullPath());
                            return;
                        }
                        
                        System.out.println("处理章节: " + section.getFullPath() + 
                                          " (页码: " + (section.getStartPage()+1) + "-" + (section.getEndPage()+1) + ")");
                        
                        // 对内容进行摘要
                        String summary;
                        try {
                            // 检查内容长度，如果太长可能需要截断
                            Tokenizer tokenizer = new OpenAiTokenizer("gpt-4o");
                            int tokenCount = tokenizer.estimateTokenCountInText(content);
                            
                            if (tokenCount > CHAT_MODEL_MAX_TOKENS - SAFETY_TOKEN_MARGIN) {
                                System.out.println("章节过长（" + tokenCount + " tokens），进行截断: " + section.getFullPath());
                                // 截取前一部分，保留一些核心内容
                                int maxChars = (CHAT_MODEL_MAX_TOKENS - SAFETY_TOKEN_MARGIN) * 4; // 估算字符数
                                content = content.substring(0, Math.min(content.length(), maxChars)) + 
                                          "\n\n[内容过长，已截断]";
                            }
                            
                            summary = interviewer.summarizeContentChunk(content);
                        } catch (Exception e) {
                            System.err.println("摘要生成失败: " + e.getMessage());
                            summary = "{\"content\":\"" + section.getTitle() + " - 内容处理失败，可能过长\",\"type\":\"摘要\",\"difficulty\":\"中等\"}";
                        }
                        
                        // 创建带有章节信息的文本段
                        Map<String, String> metadata = new HashMap<>();
                        metadata.put("section", section.getFullPath());
                        metadata.put("title", section.getTitle());
                        metadata.put("level", String.valueOf(section.getLevel()));
                        
                        // 先生成摘要，将摘要作为向量存储的内容
                        String vectorContent = interviewer.summarizeContentChunk(content);
                        System.out.println("生成摘要向量内容: " + vectorContent.substring(0, Math.min(50, vectorContent.length())) + "...");
                        
                        // 通过Content创建带有元数据的TextSegment，使用摘要内容
                        Content content1 = new Content(vectorContent);
                        TextSegment segment = content1.textSegment();
                        
                        // 生成嵌入并存储
                        try {
                            Embedding embedding = createModel.createEmbeddingModel().embed(segment).content();
                            
                            synchronized (redisEmbeddingStore) {
                                redisEmbeddingStore.add(embedding, segment);
                            }
                            
                            // 存储摘要到Redis
                            synchronized (jedis) {
                                storeInRedis(jedis, section.getFullPath(), summary);
                            }
                        } catch (Exception e) {
                            System.err.println("向量存储失败: " + e.getMessage());
                        }
                        
                        int completed = processedSections.incrementAndGet();
                        System.out.println("完成进度: " + completed + "/" + sectionsToProcess.size() + " 章节");
                        
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }, executor);
                
                futures.add(future);
            }
            
            // 等待所有任务完成
            CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
            executor.shutdown();
            
            long endTime = System.currentTimeMillis();
            double processingTime = (endTime - startTime) / 1000.0;
            
            System.out.println("PDF处理完成!");
            System.out.println("总章节数: " + allSections.size());
            System.out.println("处理章节数: " + sectionsToProcess.size());
            System.out.println("处理时间: " + processingTime + " 秒");
            
            return Result.success(String.format("处理完成! 总章节: %d, 处理章节: %d, 用时: %.2f秒", 
                                 allSections.size(), sectionsToProcess.size(), processingTime));
            
        } catch (IOException e) {
            e.printStackTrace();
            return Result.error("PDF处理失败: " + e.getMessage());
        }
    }
}
